package com.mall.thirdparth.pay.service.impl.inner;

import cn.hutool.json.JSONUtil;
import com.mall.thirdparth.pay.config.WxPayConfig;
import com.mall.thirdparth.pay.model.dto.OrderPayReqDto;
import com.mall.thirdparth.pay.model.vo.OrderPayRespVo;
import com.mall.thirdparth.pay.model.vo.OrderQueryRespVo;
import com.mall.thirdparth.pay.util.HttpServletUtils;
import com.mall.thirdparth.pay.util.WXPayUtils;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @ClassName XcxOrderPayService
 * @Author ymx
 * @Description 小程序订单支付服务
 * @Date 2023/12/22
 **/
@Service
public class XcxOrderPayService {

    private static final Logger log = LoggerFactory.getLogger(XcxOrderPayService.class);

    @Resource
    WxPayConfig wxPayConfig;

    @Resource
    private RSAAutoCertificateConfig rsaAutoCertificateConfig;

    /**
     * 创建预支付订单
     * @param payOrderReqDto
     * @param commonLog
     * @return
     * @throws Exception
     */
    public OrderPayRespVo createPrePayOrder(OrderPayReqDto payOrderReqDto, String commonLog) throws Exception {
        // 请求微信支付相关配置
        JsapiService service = new JsapiService.Builder()
                .config(rsaAutoCertificateConfig)
                .build();
        try {
            // ---------- 请求参数 ----------
            PrepayRequest request = new PrepayRequest();
            // 微信小程序appId
            request.setAppid(wxPayConfig.getAppId());
            // 商户号
            request.setMchid(wxPayConfig.getMerchantId());
            // 订单号
            request.setOutTradeNo(payOrderReqDto.getOrderNo());
            // openId
            Payer payer = new Payer();
            payer.setOpenid(payOrderReqDto.getOpenId());
            request.setPayer(payer);
            // 订单总金额
            Amount amount = new Amount();
            amount.setTotal(payOrderReqDto.getTotalPrice());
            request.setAmount(amount);
            // 订单描述
            request.setDescription(payOrderReqDto.getDesc());
            // 支付通知地址
            request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
            // 附加信息
            request.setAttach(payOrderReqDto.getOrderType());

            // ---------- 预支付下单 ----------
            log.info(commonLog + "请求参数：{}", JSONUtil.toJsonStr(request));
            // 调用预下单接口
            PrepayResponse response = service.prepay(request);
            log.info(commonLog + "创建预支付成功，返回信息：{}", response);

            // ---------- 返回响应信息 ----------
            OrderPayRespVo orderPayRespVo = new OrderPayRespVo();
            // 时间戳
            Long timeStamp = System.currentTimeMillis() / 1000;
            orderPayRespVo.setTimeStamp(timeStamp);
            // 随机字符串
            String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
            orderPayRespVo.setNonceStr(substring);
            String signatureStr = Stream.of(wxPayConfig.getAppId(), String.valueOf(timeStamp),
                            substring, "prepay_id=" + response.getPrepayId())
                    .collect(Collectors.joining("\n", "", "\n"));
            String sign = WXPayUtils.getSign(signatureStr, wxPayConfig.getPrivateKey());
            // 签名类型，默认为RSA，仅支持RSA
            orderPayRespVo.setSignType("RSA");
            // 签名
            orderPayRespVo.setPaySign(sign);
            // 预支付ID
            orderPayRespVo.setPrepayId("prepay_id=" + response.getPrepayId());
            // 订单号
            orderPayRespVo.setOrderNo(payOrderReqDto.getOrderNo());
            return orderPayRespVo;
        } catch (ServiceException e) {
            log.error(commonLog + "创建预支付订单失败", e);
            throw new com.mall.common.exception.ServiceException("下单失败");
        } catch (Exception e) {
            log.error(commonLog + "创建预支付订单失败", e);
            throw new com.mall.common.exception.ServiceException("下单失败");
        }
    }

    /**
     * 支付回调
     * @param request
     * @param commonLog
     * @return
     * @throws IOException
     */
    public Map<String, String> payNotify(HttpServletRequest request, String commonLog) throws IOException {
        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");

        // 初始化NotificationParser
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                // 若未设置signType，默认值为 WECHATPAY2-SHA256-RSA2048
                .signType(signType)
                .body(HttpServletUtils.getRequestBody(request))
                .build();
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info(commonLog + "验签成功, 支付回调结果：{}", transaction.toString());

        Map<String, String> returnMap = new HashMap<>(2);
        if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {
            log.info("商户订单号【{}】, 微信支付订单号【{}】, 支付未成功", transaction.getOutTradeNo(),
                    transaction.getTransactionId());
            returnMap.put("code", "FAIL");
            returnMap.put("message", "失败");
            return returnMap;
        }
        returnMap.put("code", "SUCCESS");
        returnMap.put("message", "成功");
        // 商户订单号
        returnMap.put("orderNo", transaction.getOutTradeNo());
        return returnMap;
    }

    /**
     * 根据商户订单号查询订单
     * @param orderNo
     * @param commonLog
     * @return
     */
    public OrderQueryRespVo queryOrder(String orderNo, String commonLog) {
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        // 商户号
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        // 商户订单号
        queryRequest.setOutTradeNo(orderNo);
        try {
            JsapiService service = new JsapiService.Builder()
                    .config(rsaAutoCertificateConfig)
                    .build();
            Transaction result = service.queryOrderByOutTradeNo(queryRequest);
            if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
                log.info(commonLog +"微信支付订单号【{}】, 支付未成功", result.getTransactionId());
            }

            OrderQueryRespVo orderQueryRespBo = new OrderQueryRespVo();
            // 商户订单号
            orderQueryRespBo.setOrderNo(result.getOutTradeNo());
            // 第三方订单号
            orderQueryRespBo.setThirdPayOrderNo(result.getTransactionId());
            // 第三方状态
            orderQueryRespBo.setThirdStatus(result.getTradeState().name());
            // 第三方交易时间
            orderQueryRespBo.setThirdSuccessTime(result.getSuccessTime());
            if(null != result.getAmount()) {
                orderQueryRespBo.setPayerTotal(result.getAmount().getPayerTotal());
                orderQueryRespBo.setTotal(result.getAmount().getTotal());
                orderQueryRespBo.setPayerCurrency(result.getAmount().getPayerCurrency());
                orderQueryRespBo.setCurrency(result.getAmount().getCurrency());
            }
            return orderQueryRespBo;
        } catch (ServiceException e) {
            log.error(commonLog + "订单查询失败, 返回码：{}, 返回信息：{}", e.getErrorCode(), e.getErrorMessage());
            return null;
        }
    }

}
